'''
* This is the projet for Brtc LlmOps Platform
* @Author Leon-liao <liaosiliang@alltman.com>
* @Description //TODO 
* @File: api_tool_service.py
* @Time: 2025/11/11
* @All Rights Reserve By Brtc
'''
import json
from dataclasses import dataclass
from typing import Any
from uuid import UUID

from injector import inject
from sqlalchemy.sql.expression import desc

from internal.core.tools.api_tools.entities.openapi_schema import OpenAPISchema
from pkg.paginator.paginator import Paginator
from pkg.sqlalchemy import SQLAlchemy
from .base_service import BaseService
from ..core.tools.api_tools.provider import ApiProviderManager
from ..exception.exception import ValidateErrorException, NotFoundException
from ..model import ApiToolProvider, ApiTool, Account
from ..schema.api_tool_schema import CreateApiToolReq, GetApiToolProviderWithPageReq, UpdateApiToolProviderReq


@inject
@dataclass
class ApiToolService(BaseService):
    """自定义 API 插件服务"""
    db:SQLAlchemy
    api_tool_provider_manager:ApiProviderManager


    @classmethod
    def parse_openapi_schema(cls, openapi_schema_str:str)->OpenAPISchema:
        """解析openapi_schema字符串， 如果出错则抛出错误"""
        try:
            data = json.loads(openapi_schema_str.strip())
            if not isinstance(data, dict):
                raise
        except Exception as e:
            raise ValidateErrorException(f"传递的数据必须符合OpenAPI的规范JOSN字符串！！{str(e)}")
        return OpenAPISchema(**data)

    def create_api_tool(self, req:CreateApiToolReq, account:Account)->None:
        """根据传递的请求创建自定义API工具"""
        #1、校验并提取Open API Schema
        openapi_schema = self.parse_openapi_schema(req.openapi_schema.data)
        #2、查询当前登陆的账号是否已经创建了同名的工具提供者, 如果创建了则抛出异常
        api_tool_provider = self.db.session.query(ApiToolProvider).filter_by(
            account_id=account.id,
            name=req.name.data
        ).one_or_none()
        if api_tool_provider:
            raise ValidateErrorException(f"该工具提供商的名字{req.name.data}已经存在当前账号下！")
        #3、首先创建工具提供者
        api_tool_provider = self.create(
            ApiToolProvider,
            account_id=account.id,
            name=req.name.data,
            icon=req.icon.data,
            description=openapi_schema.description,
            openapi_schema=req.openapi_schema.data,
            headers=req.headers.data,
        )

        #4、创建所有 工具并关联到 api_tool_provider
        for path, path_item in openapi_schema.paths.items():
            for method , method_item in path_item.items():
                self.create(
                    ApiTool,
                    account_id=account.id,
                    provider_id = api_tool_provider.id,
                    name = method_item.get("operationId"),
                    description = method_item.get("description"),
                    url = f"{openapi_schema.server}{path}",
                    method=method,
                    parameters = method_item.get("parameters", []),
                )


    def get_api_tool_providers_with_page(self, req: GetApiToolProviderWithPageReq)->tuple[list[Any], Paginator]:
        """获取自定义API工具服务提供者分页列表数据"""
        # todo:等待授权认证模块完成后进行切换调整
        account_id = "4ab64c9d-c8b4-4b80-9715-5178fd74ec11"

        #1、构建分页器查询
        paginator = Paginator(db=self.db, req=req)

        #2、构建筛选器
        filters = [ApiToolProvider.account_id == account_id]
        if req.search_word.data:
            """如果存在搜索词 就把搜索词添加到我们的过滤器里面"""
            filters.append(ApiToolProvider.name.ilike(f"%{req.search_word.data}%"))

        #3、执行分页并获取数据
        api_tool_providers = paginator.paginate(
            self.db.session.query(ApiToolProvider).filter(*filters).order_by(desc("created_at"))
        )

        return api_tool_providers, paginator


    def update_api_tool_provider(self, provider_id:UUID, req:UpdateApiToolProviderReq):
        """根据传递的provider_id  + req 更新对应的API工具提供信息"""
        # todo:等待授权认证模块完成后进行切换调整
        account_id = "4ab64c9d-c8b4-4b80-9715-5178fd74ec11"

        #1、根据传递的provider_id 查询API工具提供者信息并校验
        api_tool_provider = self.get(ApiToolProvider, provider_id)

        if api_tool_provider is None or str(api_tool_provider.account_id) != account_id:
            raise ValidateErrorException(f"该工具提供者在当前账号下不存在！")

        #2、校验Openapi schema
        openapi_schema = self.parse_openapi_schema(req.openapi_schema.data)
        #3、是检测当前帐号是否已经成功创建同名的工具 如果是则抛出错误
        check_api_tool_provider = self.db.session.query(ApiToolProvider).filter(
            ApiToolProvider.account_id == account_id,
            ApiToolProvider.name == req.name.data,
            ApiToolProvider.id != api_tool_provider.id
        ).one_or_none()

        if check_api_tool_provider:
            raise ValidateErrorException(f"该工具提供者名字 {req.name.data} 已经存在当前账号下！！")
        #4、开启数据库的自动提交
        with self.db.auto_commit():
            #5、先删除该工具提供者下的所有工具
            self.db.session.query(ApiTool).filter(
                ApiTool.id == api_tool_provider.id,
                ApiTool.account_id == account_id,
            ).delete()

        #6、修改工具提供者信息
        self.update(
            api_tool_provider,
            name = req.name.data,
            icon = req.icon.data,
            description = openapi_schema.description,
            headers = req.headers.data,
            openapi_schema = req.openapi_schema.data,
        )
        #7、新增工具信息从而完成覆盖更新
        for path, path_item in openapi_schema.paths.items():
            for method, method_item in path_item.items():
                self.create(
                    ApiTool,
                    account_id=account_id,
                    provider_id=api_tool_provider.id,
                    name=method_item.get("operationId"),
                    description=method_item.get("description"),
                    url=f"{openapi_schema.server}{path}",
                    method=method,
                    parameters=method_item.get("parameters", []),
                )


    def get_api_tool_provider(self, provider_id: UUID) -> ApiToolProvider:
        """根据传递的provider_id获取API工具提供者信息"""
        # todo:等待授权认证模块完成进行切换调整
        account_id = "4ab64c9d-c8b4-4b80-9715-5178fd74ec11"
        # 1.查询数据库获取对应的数据
        api_tool_provider = self.get(ApiToolProvider, provider_id)
        # 2.检验数据是否为空，并且判断该数据是否属于当前账
        if api_tool_provider is None or str(api_tool_provider.account_id) != account_id:
            raise NotFoundException("该工具提供者不存在")
        return api_tool_provider


    def get_api_tool(self, provider_id: UUID, tool_name: str) -> ApiTool:
        """根据传递的provider_id+tool_name获取对应工具的参数详情信息"""
        # todo:等待授权认证模块完成进行切换调整
        account_id = "e036e651-9eae-4b15-8dc3-b9d692121acf"
        api_tool = self.db.session.query(ApiTool).filter_by(
            provider_id=provider_id,
            name=tool_name,
        ).one_or_none()
        if api_tool is None or str(api_tool.account_id) != account_id:
            raise NotFoundException("该工具不存在")
        return api_tool


    def delete_api_tool_provider(self, provider_id: UUID):
        """根据传递的provider_id删除对应的工具提供商+工具的所有信息"""
        # todo:等待授权认证模块完成进行切换调整
        account_id = "4ab64c9d-c8b4-4b80-9715-5178fd74ec11"
        # 1.先查找数据，检测下provider_id对应的数据是否存在，权限是否正确
        api_tool_provider = self.get(ApiToolProvider, provider_id)
        if api_tool_provider is None or str(api_tool_provider.account_id) != account_id:
            raise NotFoundException("该工具提供者不存在")
        # 2.开启数据库的自动提交
        with self.db.auto_commit():
            # 3.先来删除提供者对应的工具信息
            self.db.session.query(ApiTool).filter(
                ApiTool.provider_id == provider_id,
                ApiTool.account_id == account_id,
            ).delete()
            # 4.删除服务提供者
            self.db.session.delete(api_tool_provider)


    def api_tool_invoke(self):
        provider_id = "d5dcbfe2-fea8-471f-9429-c48941d930af"
        tool_name = "YoudaoSuggest"
        api_tool = self.db.session.query(ApiTool).filter(
            ApiTool.provider_id == provider_id,
            ApiTool.name == tool_name
        ).one_or_none()

        api_tool_provider = api_tool.provider
        from internal.core.tools.api_tools.entities.tool_entities import ToolEntity
        tool = self.api_tool_provider_manager.get_tool(ToolEntity(
            id = provider_id,
            name = tool_name,
            url=api_tool.url,
            method=api_tool.method,
            description=api_tool.description,
            headers=api_tool_provider.headers,
            parameters=api_tool.parameters,
        ))
        return tool.invoke({"q":"love", "doctype":"json"})
